﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using ShadowEngine;
using Tao.OpenGl; 
using System.Windows.Forms;  
using ShadowEngine.ContentLoading;
using System.Timers;

namespace CarRace
{
    public class Car : Terminal
    {
        // Simulation of a cloud
        public Cloud MessageCloud;
        // Storing messages
        public List<CarGotten> CarGotten_List = new List<CarGotten>();
        // Property of Timer
        private static System.Timers.Timer aTimer;
        // Car ID
        public int id;

        // socket variables
        MainForm form;

        // Warning
        public string warning = "";

        float tireAngle;
        //static int carColor = 1;
        Color color;
        public Position origin_pos;
        float angle;
        //Position current_pos; // Already in terminal abstract
        public float speed_x;
        public float speed_y;
        public float traveledDistance_x = 0;
        public float traveledDistance_y = 0;

        public float last_pred_speed_x;
        public float last_pred_speed_y;
        //static Random randomizer = new Random();
        int counter;
        ModelContainer m;
        
        List<Mesh> tires = new List<Mesh>();
        Mesh body;
        
        int texture;

        public float TraveledDistance
        {
            get { return traveledDistance_y; }
        }

        public Position GetPosition()
        {
            return current_pos;
        }

        public Car(int _id, Position _pos, float _angle, Color _color, float _speed_x, float _speed_y, Cloud Cloud, MainForm _form)
        {


            //udp socket variables
            form = _form;
                
            // Create a Cloud
            MessageCloud = Cloud;
            // Join a Terminal list
            Cloud.Join(this);
            // Create a timer for receiver and transmitter
            aTimer = new System.Timers.Timer(25);
            // Hook up the Elapsed event for the timer. 
            aTimer.Elapsed += OnTimedEvent;
            aTimer.Enabled = true;

            /*if (carColor == 1)
            {
                color = Color.Blue;
                origin_pos = new Position(0, 10);  
            }
            if (carColor == 2)
            {
                color = Color.Red;
                origin_pos = new Position(1.5f, 10);  
            }
            if (carColor == 3)
            {
                color = Color.Black;
                origin_pos = new Position(3, 10);
            }*/
            id = _id;
            color = _color;
            origin_pos = _pos;
            current_pos = _pos;
            speed_x = _speed_x;
            speed_y = _speed_y;
            angle = _angle;
            //carColor++;
            //speed = 0.2f + (float)randomizer.NextDouble() / 18f;
            m = ContentManager.GetModelByName("carro.3DS");
            m.CreateDisplayList(); // optimice the model and load it in opengl display lists

            m.ScaleX = 0.1f;
            m.ScaleY = 0.1f;
            m.ScaleZ = 0.1f;

            foreach (var item in m.GetMeshes)
            {
                item.CalcCenterPoint(); // calculate the joint point
                switch (item.Name)
                {
                    case "tireA":
                    case "tireB":
                    case "tireC":
                    case "tire":
                    case "rimA":
                    case "rimB":
                    case "rimC":
                    case "rim":
                        tires.Add(item);
                        break;
                    case "body":
                        body = item;
                        break;
                }
            }

            if (color == Color.Blue)
            {
                texture = ContentManager.GetTextureByName("bodyBlue.jpg");
            }
            if (color == Color.Red)
            {
                texture = ContentManager.GetTextureByName("bodyRed.jpg");
            }
            if (color == Color.Black)
            {
                texture = ContentManager.GetTextureByName("bodyBlack.jpg");
                m.RemoveMeshesWithName("body");
                m.RemoveMeshesWithName("tire");
                m.RemoveMeshesWithName("rim");
            }
        }

        public void ResetRace()
        {
            traveledDistance_x = 0;
            traveledDistance_y = 0;
            current_pos = origin_pos;
        }

        public void Create()
        {
            /*m = ContentManager.GetModelByName("carro.3DS");
            m.CreateDisplayList(); //optimice the model and load it in opengl display lists
            
            m.ScaleX = 0.1f;
            m.ScaleY = 0.1f;
            m.ScaleZ = 0.1f;

            foreach (var item in m.GetMeshes)
            {
                item.CalcCenterPoint();// calcula el punto de pivote
                switch (item.Name)
                {
                    case "tireA":
                    case "tireB":
                    case "tireC":
                    case "tire":
                    case "rimA":
                    case "rimB":
                    case "rimC":
                    case "rim":
                        tires.Add(item);
                        break; 
                    case "body":
                         body = item;
                         break;
                } 
            }

            if (color == Color.Blue)
            {
                texture = ContentManager.GetTextureByName("bodyBlue.jpg");   
            }
            if (color == Color.Red)
            {
                texture = ContentManager.GetTextureByName("bodyRed.jpg");
            }
            if (color == Color.Black)
            {
                texture = ContentManager.GetTextureByName("bodyBlack.jpg");
                m.RemoveMeshesWithName("body");
                m.RemoveMeshesWithName("tire");
                m.RemoveMeshesWithName("rim");
            }*/
        }
        private void OnTimedEvent(Object source, ElapsedEventArgs e)
        {
            //MessageCloud.Send(this, id, GetPosition().x, GetPosition().y, DateTime.Now.Second*1000 + DateTime.Now.Millisecond);

            foreach (CarGotten car in this.CarGotten_List)
            {
                // Check potential collision
                float x_speed = 0, y_speed = 0;
                float c = 0, b = 0, d = 0;
                int count = car.Position_List.Count;
                //foreach (PositionGotten el in car.Position_List)
                for(int i=0; i<count; i++)
                {
                    PositionGotten el = car.Position_List[i];
                    float time = (el.time - car.Position_List[0].time);
                    float x = (el.x - car.Position_List[0].x);
                    float y = (el.y - car.Position_List[0].y);
                    c += time * time;
                    b += time * x;
                    d += time * y;
                }
                x_speed = b / c * 25; //per tick
                y_speed = d / c * 25; //per tick

                last_pred_speed_x = x_speed;
                last_pred_speed_y = y_speed;

                float t1=0, t2=0;
                bool inf1 = false, inf2 = false;
                if((x_speed - speed_x) != 0)
                {
                    t1 = (current_pos.x - car.Position_List[car.Position_List.Count - 1].x) / (x_speed - (-speed_x));
                }
                else
                {
                    inf1 = true;
                }
                if((y_speed - speed_y) != 0)
                {
                    t2 = (current_pos.y - car.Position_List[car.Position_List.Count - 1].y) / (y_speed - (-speed_y));
                }
                else
                {
                    inf2 = true;
                }

                //MessageBox.Show(inf1.ToString() + " " + inf2.ToString() + " " +t1.ToString()+" "+t2.ToString());
                // check
                //if((inf1 == true && t2 < 0) || (inf2 == true && t1 < 0) // never collision
                //MessageBox.Show("potential collision");
                if(t1 >= 0 && t2 >= 0 && Math.Abs(t2 - t1) <= 100)
                {
                    //System.IO.StreamWriter file = new System.IO.StreamWriter("test3.txt");
                    //file.WriteLine(inf1.ToString() + " " + inf2.ToString() + " " + t1.ToString() + " " + t2.ToString() + "\r\n myx=" + current_pos.x.ToString() + "\r\n hisx=" + car.Position_List[0].x.ToString() + "\r\n hisxspeed=" + x_speed.ToString() + "\r\n myxspeed=" + speed_x.ToString() + "\r\n myy=" + current_pos.y.ToString() + "\r\n hisy=" + car.Position_List[0].y.ToString() + "\r\n hisyspeed=" + y_speed.ToString() + "\r\n myyspeed=" + speed_y.ToString() + "\r\n");
                    //file.Close();
                    warning = "Potential collision";
                    //MessageBox.Show("potential collision");
                    //System.IO.StreamWriter file = new System.IO.StreamWriter("test3.txt");
                    //file.WriteLine(inf1.ToString() + " " + inf2.ToString() + " " + t1.ToString() + " " + t2.ToString() + "\r\n");
                    //file.Close();
                }else
                    warning = "";
            }
        }

        public void Read(int id, float x, float y, int time, int mes = 0)
        {
            bool stored = false;
            foreach (CarGotten element in CarGotten_List)
            {
                if (element.id == id)
                {
                    element.StorePosition(x, y, time);
                    stored = true;
                }
            }
            if(stored == false)
            {
                CarGotten new_cargotten = new CarGotten(id);
                new_cargotten.StorePosition(x, y, time);
                CarGotten_List.Add(new_cargotten);

            }
        }
        public void ClearCarGottenList()
        {
            CarGotten_List = new List<CarGotten>();
        }
        public void Draw(bool stop)
        {
            Gl.glPushMatrix();
            Gl.glTranslatef(origin_pos.x, 0, origin_pos.y);
            Gl.glRotatef(angle, 0, 1.0f, 0);

            #region race logic

            if (Controller.StartedRace == true)
            {
                //move the object the traveled distance
                if(angle == 0)
                    Gl.glTranslatef(traveledDistance_x, 0, traveledDistance_y);
                else
                    Gl.glTranslatef(traveledDistance_y, 0, traveledDistance_x); // because it was rotated
                //if (traveledDistance_y > -59)
                //{
                if(stop == false)
                {
                    // 3d float speed to km/h -> 3dfs * 216
                    tireAngle -= 24;
                    traveledDistance_x -= speed_x;
                    traveledDistance_y -= speed_y;
                    current_pos.x -= speed_x;
                    current_pos.y -= speed_y;
                 }
            }

            #endregion

            m.DrawWithTextures(); // draw the car

            #region draw body

            Gl.glPushMatrix();
            Gl.glScalef(0.1f, 0.1f, 0.1f);

            Gl.glEnable(Gl.GL_TEXTURE_2D);// enable textures
            Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture);
            body.Draw();

            Gl.glDisable(Gl.GL_TEXTURE_2D);
            Gl.glPopMatrix(); 

            #endregion

            #region draw rolling tires

            foreach (var item in tires)
            {
                Gl.glColor3f(0.5f, 0.5f, 0.5f); //specify the color of the wheels
                Gl.glPushMatrix();
                Gl.glScalef(0.1f, 0.1f, 0.1f);
                Gl.glTranslatef(item.CenterPoint.X, item.CenterPoint.Y, item.CenterPoint.Z); // translate the original position 
                Gl.glRotatef(tireAngle, 1, 0, 0);// se rota 
                Gl.glTranslatef(-item.CenterPoint.X, -item.CenterPoint.Y, -item.CenterPoint.Z); // translate the center
                item.Draw();  // draw the wheels
                Gl.glPopMatrix();
                Gl.glColor3f(1, 1, 1); 
            }

            #endregion

            Gl.glPopMatrix();
        }
    }
}
